/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining matrix1 copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR matrix1 PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

namespace Vj.MathLibrary.Matrix
{
    public class MatrixDouble 
    {
        double[,] data;

        #region Constructors

        public MatrixDouble()
        {
            throw new InvalidOperationException("MatrixDouble default constructor not implemented.");
        }

        public MatrixDouble(double[,] newMatrix)
        {
            if (newMatrix == null)
                throw new ArgumentNullException();

            data = newMatrix;
        }

        public MatrixDouble(Vector newVector)
        {
            if (newVector == null)
                throw new ArgumentNullException();

            data = new double[1, newVector.Length];
            for (int c = 0; c < newVector.Length; c++)
            {
                data[0, c] = newVector[c];
            }
        }

        public MatrixDouble(int numOfRows, int numOfCols)
        {
            if (numOfRows <= 0 || numOfCols <= 0 )
                throw new ArgumentOutOfRangeException();

            data = new double[numOfRows, numOfCols];
            for (int i = 0; i < numOfRows; i++)
                for (int j = 0; j < numOfCols; j++)
                    data[i,j] = 0;
        }

        #endregion

        #region Properties / Indexer / ToString

        //Number of rows
        public int Rows
        {
            get { return data.GetLength(0); }
        }

        //Number of columns
        public int Columns
        {
            get { return data.GetLength(1); }
        }

        public double this[int rowIndex, int colIndex]
        {
            get
            {
                if (rowIndex > Rows || colIndex > Columns)
                    throw new IndexOutOfRangeException();

                return data[rowIndex, colIndex];
            }
            set
            {
                if (rowIndex > Rows || colIndex > Columns)
                    throw new IndexOutOfRangeException();

                data[rowIndex, colIndex] = value;
            }
        }

        public override string ToString()
        {
            string output = "";
            for (int i = 0; i < this.Rows; i++)
            {
                output += "{ ";
                for (int j = 0; j < this.Columns; j++)
                    output += data[i, j] + " ";

                output += "}\n";
            }

            return output;
        }


        #endregion

        #region Operators

        public static MatrixDouble operator +(MatrixDouble matrix1, MatrixDouble matrix2)
        {
            if (matrix1 == null || matrix2 == null)
                throw new ArgumentNullException();

            if (!((matrix1.Columns == matrix2.Columns) && (matrix1.Rows == matrix2.Rows)))
                throw new ArgumentOutOfRangeException();

            MatrixDouble returnMatrix = new MatrixDouble(new double[matrix1.Rows, matrix1.Columns]);

            for (int i = 0; i < matrix1.Rows; i++)
                for (int j = 0; j < matrix1.Columns; j++)
                    returnMatrix[i, j] = matrix1[i, j] + matrix2[i, j];

            return returnMatrix;
        }
           
        public static MatrixDouble operator -(MatrixDouble matrix1, MatrixDouble matrix2)
        {
            if (matrix1 == null || matrix2 == null)
                throw new ArgumentNullException();

            if (!((matrix1.Columns == matrix2.Columns) && (matrix1.Rows == matrix2.Rows)))
                throw new ArgumentOutOfRangeException();

            MatrixDouble returnMatrix = new MatrixDouble(new double[matrix1.Rows, matrix1.Columns]);

            for (int i = 0; i < matrix1.Rows; i++)
                for (int j = 0; j < matrix1.Columns; j++)
                    returnMatrix[i, j] = matrix1[i, j] - matrix2[i, j];

            return returnMatrix;
        }

        public static MatrixDouble operator *(MatrixDouble matrix1, MatrixDouble matrix2)
        {
            if (matrix1 == null || matrix2 == null)
                throw new ArgumentNullException();

            if (matrix1.Columns != matrix2.Rows)
                throw new ArgumentOutOfRangeException();

            MatrixDouble returnMatrix = new MatrixDouble(new double[matrix1.Rows, matrix2.Columns]);

            for (int row = 0; row < matrix1.Rows; row++)
                for (int column = 0; column < matrix2.Columns; column++)
                    for (int z = 0; z < matrix1.Columns; z++)
                        returnMatrix[row, column] += matrix1[row, z] * matrix2[z, column];

            return returnMatrix;
        }

        public static MatrixDouble operator *(double c, MatrixDouble aMatrix)
        {
            if (aMatrix == null)
                throw new ArgumentNullException();

            MatrixDouble returnMatrix = new MatrixDouble(new double[aMatrix.Rows, aMatrix.Columns]);

            for (int row = 0; row < aMatrix.Rows; row++)
                for (int column = 0; column < aMatrix.Columns; column++)
                    returnMatrix[row, column] = c*aMatrix[row, column]; 
         
            return returnMatrix;
        }

        public MatrixDouble Transpose()
        {
            MatrixDouble returnMatrix = new MatrixDouble(new double[this.Columns, this.Rows]);

            for (int row = 0; row < this.Rows; row++)
                for (int column = 0; column < this.Columns; column++)
                    returnMatrix[column, row] = this[row, column]; 

            return returnMatrix;
        }

        #endregion
    }
}
